<?php
//https://www.php.net/curl_setopt
class Curl {
  protected $ch;
  protected $data;
  protected $debug;

  public function __construct( $debug = 0, $timeout = 30 ) {
    $this->debug = $debug;
    $this->log( "\r\n==============================================================================\r\n\r\n", 1 );
    $this->log( 'cUrl Init...' );
    $this->ch = curl_init();
    $version = curl_version();
    $this->log( 'cUrl Version: ' . $version[ 'version' ] );
    curl_setopt( $this->ch, CURLOPT_TIMEOUT, $timeout );
    curl_setopt( $this->ch, CURLOPT_SSL_VERIFYPEER, 0 );
    curl_setopt( $this->ch, CURLOPT_SSL_VERIFYHOST, 0 );
  }

  public function set_head( $head = 0 ) {
    if ( $head ) {
      curl_setopt( $this->ch, CURLOPT_HTTPHEADER, $head );
      foreach ( $head as $val ) {
        $this->log( $val );
      }
    }
  }

  public function set_cookie( $cookie = 0 ) {
    if ( $cookie ) {
      curl_setopt( $this->ch, CURLOPT_COOKIE, $cookie );
      $this->log( 'Set-Cookie: ' . $cookie );
    }
  }

  public function user_agent( $agent = 0 ) {
    if ( !$agent ) {
      $agent = $this->get_useragent();
    }
    curl_setopt( $this->ch, CURLOPT_USERAGENT, $agent );
    $this->log( 'User-Agent: ' . $agent );
  }

  public function set_referer( $url = '' ) {
    if ( empty( $url ) ) {
      curl_setopt( $this->ch, CURLOPT_AUTOREFERER, 1 );
    } else {
      $this->log( 'Referer: ' . $url );
      curl_setopt( $this->ch, CURLOPT_REFERER, $url );
    }

  }

  public function load_cookie( $path ) {
    $this->log( 'Load Cookie: ' . $path );
    curl_setopt( $this->ch, CURLOPT_COOKIEJAR, $path );
  }

  public function save_cookie( $path ) {
    $this->log( 'Save Cookie: ' . $path );
    curl_setopt( $this->ch, CURLOPT_COOKIEFILE, $path );
  }

  public function head( $head = 0, $body = 0 ) {
    curl_setopt( $this->ch, CURLOPT_HEADER, $head );
    curl_setopt( $this->ch, CURLOPT_NOBODY, $body );
  }

  public function proxy( $proxy, $port = 0, $user = '', $pswd = '', $auth = 'CURLAUTH_BASIC', $type = 'CURLPROXY_HTTP' ) {

    curl_setopt( $this->ch, CURLOPT_PROXYAUTH, $auth ); //代理认证模式 CURLAUTH_BASIC和CURLAUTH_NTLM
    curl_setopt( $this->ch, CURLOPT_PROXY, $proxy ); //代理服务器地址
    if ( $port ) {
      curl_setopt( $this->ch, CURLOPT_PROXYPORT, $port ); //代理服务器端口
    }

    //curl_setopt( $this->ch, CURLOPT_PROXYUSERPWD, "$user:$pswd" ); //http代理认证帐号，名称:pwd的格式
    curl_setopt( $this->ch, CURLOPT_PROXYTYPE, $type ); //使用http代理模式 CURLPROXY_HTTP (默认值) 就是CURLPROXY_SOCKS5
    $this->log( 'Proxy Ip: ' . $proxy );
  }

  public function get( $url, $jump = 1, $head = 0 ) {
    $this->log( 'Request Method: GET' );
    return $this->execute( $url, $jump, $head );
  }

  public function get_json( $url, $jump = 1, $head = 0 ) {
    $data = $this->get( $url, $jump, $head );
    return json_decode( $data, true );
  }

  public function post( $url, $field, $jump = 1, $head = 0 ) {
    $this->log( 'Request Method: POST' );
    curl_setopt( $this->ch, CURLOPT_POST, 1 );
    curl_setopt( $this->ch, CURLOPT_POSTFIELDS, $field );
    return $this->execute( $url, $jump, $head );
  }

  public function post_json( $url, $field, $jump = 1, $head = 0 ) {
    $data = $this->post( $url, $field, $jump, $head );
    return json_decode( $data, true );
  }

  public function upload( $url, $field, $file, $jump = 1 ) {
    $this->log( 'Request Method: POST' );
    $file = new CURLFile( $file );
    curl_setopt( $this->ch, CURLOPT_POST, 1 );
    curl_setopt( $this->ch, CURLOPT_POSTFIELDS, [ $field => $file ] );
    return $this->execute( $url, $jump, 0 );
  }

  public function download( $url, $path ) {
    $this->log( 'Request Method: GET' );
    $this->head( 1, 0 );
    $temp = $this->execute( $url, 1, 0 );

    $info = $this->getinfo();
    //print_r(strlen( $temp ));die;
    $code = [ 'http_code' ];
    $size = [ 'size_download' ];
    $type = [ 'content_type' ];

    $dir = pathinfo( $path, PATHINFO_DIRNAME );
    !is_dir( $dir ) && @mkdir( $dir, 0777, true );

    $file = file_put_contents( $path, $temp );
    if ( $file == $size ) {
      $this->log( 'Success.' );
    } else {
      $this->log( 'Fail.' );
    }


  }

  public function get_cookie( $url, $jump = 1 ) {
    $this->log( 'Request Method: GET' );
    $data = $this->get( $url, $jump, 1 );
    return $this->match_cookie( $data );
  }

  public function post_cookie( $url, $field, $jump = 1 ) {
    $this->log( 'Request Method: POST' );
    $data = $this->post( $url, $field, $jump, 1 );
    return $this->match_cookie( $data );
  }

  public function match_cookie( $data ) {
    preg_match_all( '/Set-Cookie: (.*);/iU', $data, $cookie );
    return implode( ';', $cookie[ 1 ] );
  }

  public function execute( $url, $jump, $head ) {
    $this->log( 'Request URL: ' . $url );
    if ( $head ) {
      $this->head( 1, 1 );
    } else {
      $this->head();
    }
    curl_setopt( $this->ch, CURLOPT_FOLLOWLOCATION, $jump );
    curl_setopt( $this->ch, CURLOPT_URL, $url );

    $this->log( "\r\n------------------------------ Response Headers ------------------------------\r\n", 1 );

    curl_setopt( $this->ch, CURLOPT_WRITEFUNCTION, [ $this, 'DataCallback' ] );
    curl_setopt( $this->ch, CURLOPT_HEADERFUNCTION, [ $this, 'HeaderCallback' ] );
    curl_setopt( $this->ch, CURLOPT_RETURNTRANSFER, 1 );

    curl_setopt( $this->ch, CURLINFO_HEADER_OUT, 1 );
    curl_setopt( $this->ch, CURLOPT_VERBOSE, 1 );
    curl_setopt( $this->ch, CURLOPT_ENCODING, '' );
    curl_setopt( $this->ch, CURLOPT_FAILONERROR, 1 );

    $this->data = curl_exec( $this->ch );

    $info = $this->getinfo();
    //return $info;

    if ( isset( $info[ 'request_header' ] ) ) {
      $this->log( "\r\n------------------------------ Request Headers ------------------------------\r\n\r\n", 1 );
      $this->log( $info[ 'request_header' ], 1 );
    }

    $this->log( 'Status Code: ' . $info[ 'http_code' ] );
    $this->log( 'Content Length: ' . $info[ 'size_download' ] );
    $this->log( 'Content Type: ' . $info[ 'content_type' ] );
    $this->log( 'Remote Address: ' . $info[ 'primary_ip' ] . ':' . $info[ 'primary_port' ] );
    $this->log( 'Total Time: ' . $info[ 'total_time' ] );

    $errno = $this->errno();
    // return var_dump($this->ch );
    if ( $errno === CURLE_OK ) {
      $this->log( 'Success.' );
      return $this->data;
    } else {
      $this->log( 'Error: ' . $errno );
      return $errno;
    }
  }

  public function getinfo() {
    return curl_getinfo( $this->ch );
  }

  public function HeaderCallback( $ch, $data ) {
    $msg = str_replace( PHP_EOL, '', $data );
    $this->log( "\r\n" . trim( $msg ), 1 );
    return strlen( $data );
  }

  public function DataCallback( $ch, $data ) {
    //file_put_contents( __DIR__ . '/data.txt', $data, FILE_APPEND );

    $this->responseXML .= $data;
    $length = strlen( $data );
    // $this->size += $this->length;

    return strlen( $data );
  }

  public function cut( $start, $end, $index = 1 ) {
    if ( empty( $this->data ) ) return '错误：内容为空！';
    $before = explode( $start, $this->data );

    if ( !isset( $before[ $index ] ) ) return '错误：没有找到指定内容！';
    $this->data = explode( $end, $before[ $index ] );
    return $this->data[ 0 ];
  }

  public function errno() {
    $info = [
      '1' => 'CURLE_UNSUPPORTED_PROTOCOL (1) – 您传送给 libcurl 的网址使用了此 libcurl 不支持的协议。 可能是您没有使用的编译时选项造成了这种情况（可能是协议字符串拼写有误，或没有指定协议 libcurl 代码）。',
      '2' => 'CURLE_FAILED_INIT (2) – 非常早期的初始化代码失败。 可能是内部错误或问题。',
      '3' => 'CURLE_URL_MALFORMAT (3) – 网址格式不正确。',
      '5' => 'CURLE_COULDNT_RESOLVE_PROXY (5) – 无法解析代理服务器。 指定的代理服务器主机无法解析。',
      '6' => 'CURLE_COULDNT_RESOLVE_HOST (6) – 无法解析主机。 指定的远程主机无法解析。',
      '7' => 'CURLE_COULDNT_CONNECT (7) – 无法通过 connect() 连接至主机或代理服务器。',
      '8' => 'CURLE_FTP_WEIRD_SERVER_REPLY (8) – 在连接到 FTP 服务器后，libcurl 需要收到特定的回复。 此错误代码表示收到了不正常或不正确的回复。 指定的远程服务器可能不是正确的 FTP 服务器。',
      '9' => 'CURLE_REMOTE_ACCESS_DENIED (9) – 我们无法访问网址中指定的资源。 对于 FTP，如果尝试更改为远程目录，就会发生这种情况。',
      '11' => 'CURLE_FTP_WEIRD_PASS_REPLY (11) – 在将 FTP 密码发送到服务器后，libcurl 需要收到正确的回复。 此错误代码表示返回的是意外的代码。',
      '13' => 'CURLE_FTP_WEIRD_PASV_REPLY (13) – libcurl 无法从服务器端收到有用的结果，作为对 PASV 或 EPSV 命令的响应。 服务器有问题。',
      '14' => 'CURLE_FTP_WEIRD_227_FORMAT (14) – FTP 服务器返回 227 行作为对 PASV 命令的响应。如果 libcurl 无法解析此行，就会返回此代码。',
      '15' => 'CURLE_FTP_CANT_GET_HOST (15) – 在查找用于新连接的主机时出现内部错误。',
      '17' => 'CURLE_FTP_COULDNT_SET_TYPE (17) – 在尝试将传输模式设置为二进制或 ascii 时发生错误。',
      '18' => 'CURLE_PARTIAL_FILE (18) – 文件传输尺寸小于或大于预期。当服务器先报告了一个预期的传输尺寸，然后所传送的数据与先前指定尺寸不相符时，就会发生此错误。',
      '19' => 'CURLE_FTP_COULDNT_RETR_FILE (19) – ‘RETR’ 命令收到了不正常的回复，或完成的传输尺寸为零字节。',
      '21' => 'CURLE_QUOTE_ERROR (21) – 在向远程服务器发送自定义 “QUOTE” 命令时，其中一个命令返回的错误代码为 400 或更大的数字（对于 FTP），或以其他方式表明命令无法成功完成。',
      '22' => 'CURLE_HTTP_RETURNED_ERROR (22) – 如果 CURLOPT_FAILONERROR 设置为 TRUE，且 HTTP 服务器返回 >= 400 的错误代码，就会返回此代码。 （此错误代码以前又称为 CURLE_HTTP_NOT_FOUND。）',
      '23' => 'CURLE_WRITE_ERROR (23) – 在向本地文件写入所收到的数据时发生错误，或由写入回调 (write callback) 向 libcurl 返回了一个错误。',
      '25' => 'CURLE_UPLOAD_FAILED (25) – 无法开始上传。 对于 FTP，服务器通常会拒绝执行 STOR 命令。错误缓冲区通常会提供服务器对此问题的说明。 （此错误代码以前又称为 CURLE_FTP_COULDNT_STOR_FILE。）',
      '26' => 'CURLE_READ_ERROR (26) – 读取本地文件时遇到问题，或由读取回调 (read callback) 返回了一个错误。',
      '27' => 'CURLE_OUT_OF_MEMORY (27) – 内存分配请求失败。此错误比较严重，若发生此错误，则表明出现了非常严重的问题。',
      '28' => 'CURLE_OPERATION_TIMEDOUT (28) – 操作超时。 已达到根据相应情况指定的超时时间。',
      '30' => 'CURLE_FTP_PORT_FAILED (30) – FTP PORT 命令返回错误。 在没有为 libcurl 指定适当的地址使用时，最有可能发生此问题。 请参阅 CURLOPT_FTPPORT。',
      '31' => 'CURLE_FTP_COULDNT_USE_REST (31) – FTP REST 命令返回错误。如果服务器正常，则应当不会发生这种情况。',
      '33' => 'CURLE_RANGE_ERROR (33) – 服务器不支持或不接受范围请求。',
      '34' => 'CURLE_HTTP_POST_ERROR (34) – 此问题比较少见，主要由内部混乱引发。',
      '35' => 'CURLE_SSL_CONNECT_ERROR (35) – 同时使用 SSL/TLS 时可能会发生此错误。您可以访问错误缓冲区查看相应信息，其中会对此问题进行更详细的介绍。可能是证书（文件格式、路径、许可）、密码及其他因素导致了此问题。',
      '36' => 'CURLE_FTP_BAD_DOWNLOAD_RESUME (36) – 尝试恢复超过文件大小限制的 FTP 连接。',
      '37' => 'CURLE_FILE_COULDNT_READ_FILE (37) – 无法打开 FILE:// 路径下的文件。原因很可能是文件路径无法识别现有文件。 建议您检查文件的访问权限。',
      '38' => 'CURLE_LDAP_CANNOT_BIND (38) – LDAP 无法绑定。LDAP 绑定操作失败。',
      '39' => 'CURLE_LDAP_SEARCH_FAILED (39) – LDAP 搜索无法进行。',
      '41' => 'CURLE_FUNCTION_NOT_FOUND (41) – 找不到函数。 找不到必要的 zlib 函数。',
      '42' => 'CURLE_ABORTED_BY_CALLBACK (42) – 由回调中止。 回调向 libcurl 返回了 “abort”。',
      '43' => 'CURLE_BAD_FUNCTION_ARGUMENT (43) – 内部错误。 使用了不正确的参数调用函数。',
      '45' => 'CURLE_INTERFACE_FAILED (45) – 界面错误。 指定的外部界面无法使用。 请通过 CURLOPT_INTERFACE 设置要使用哪个界面来处理外部连接的来源 IP 地址。 （此错误代码以前又称为 CURLE_HTTP_PORT_FAILED。）',
      '47' => 'CURLE_TOO_MANY_REDIRECTS (47) – 重定向过多。 进行重定向时，libcurl 达到了网页点击上限。请使用 CURLOPT_MAXREDIRS 设置上限。',
      '48' => 'CURLE_UNKNOWN_TELNET_OPTION (48) – 无法识别以 CURLOPT_TELNETOPTIONS 设置的选项。 请参阅相关文档。',
      '49' => 'CURLE_TELNET_OPTION_SYNTAX (49) – telnet 选项字符串的格式不正确。',
      '51' => 'CURLE_PEER_FAILED_VERIFICATION (51) – 远程服务器的 SSL 证书或 SSH md5 指纹不正确。',
      '52' => 'CURLE_GOT_NOTHING (52) – 服务器未返回任何数据，在相应情况下，未返回任何数据就属于出现错误。',
      '53' => 'CURLE_SSL_ENGINE_NOTFOUND (53) – 找不到指定的加密引擎。',
      '54' => 'CURLE_SSL_ENGINE_SETFAILED (54) – 无法将选定的 SSL 加密引擎设为默认选项。',
      '55' => 'CURLE_SEND_ERROR (55) – 无法发送网络数据。',
      '56' => 'CURLE_RECV_ERROR (56) – 接收网络数据失败。',
      '58' => 'CURLE_SSL_CERTPROBLEM (58) – 本地客户端证书有问题',
      '59' => 'CURLE_SSL_CIPHER (59) – 无法使用指定的密钥',
      '60' => 'CURLE_SSL_CACERT (60) – 无法使用已知的 CA 证书验证对等证书',
      '61' => 'CURLE_BAD_CONTENT_ENCODING (61) – 无法识别传输编码',
      '62' => 'CURLE_LDAP_INVALID_URL (62) – LDAP 网址无效',
      '63' => 'CURLE_FILESIZE_EXCEEDED (63) – 超过了文件大小上限',
      '64' => 'CURLE_USE_SSL_FAILED (64) – 请求的 FTP SSL 级别失败',
      '65' => 'CURLE_SEND_FAIL_REWIND (65) – 进行发送操作时，curl 必须回转数据以便重新传输，但回转操作未能成功',
      '66' => 'CURLE_SSL_ENGINE_INITFAILED (66) – SSL 引擎初始化失败',
      '67' => 'CURLE_LOGIN_DENIED (67) – 远程服务器拒绝 curl 登录（7.13.1 新增功能）',
      '68' => 'CURLE_TFTP_NOTFOUND (68) – 在 TFTP 服务器上找不到文件',
      '69' => 'CURLE_TFTP_PERM (69) – 在 TFTP 服务器上遇到权限问题',
      '70' => 'CURLE_REMOTE_DISK_FULL (70) – 服务器磁盘空间不足',
      '71' => 'CURLE_TFTP_ILLEGAL (71) – TFTP 操作非法',
      '72' => 'CURLE_TFTP_UNKNOWNID (72) – TFTP 传输 ID 未知',
      '73' => 'CURLE_REMOTE_FILE_EXISTS (73) – 文件已存在，无法覆盖',
      '74' => 'CURLE_TFTP_NOSUCHUSER (74) – 运行正常的 TFTP 服务器不会返回此错误',
      '75' => 'CURLE_CONV_FAILED (75) – 字符转换失败',
      '76' => 'CURLE_CONV_REQD (76) – 调用方必须注册转换回调',
      '77' => 'CURLE_SSL_CACERT_BADFILE (77) – 读取 SSL CA 证书时遇到问题（可能是路径错误或访问权限问题）',
      '78' => 'CURLE_REMOTE_FILE_NOT_FOUND (78) – 网址中引用的资源不存在',
      '79' => 'CURLE_SSH (79) – SSH 会话中发生无法识别的错误',
      '80' => 'CURLE_SSL_SHUTDOWN_FAILED (80) – 无法终止 SSL 连接'
    ];
    $result = curl_errno( $this->ch );
    if ( $result === CURLE_OK ) {
      return $result;
    } else {
      return $info[ $result ];
    }

  }

  public function random_ip() {
    $ip = $this->get_ip();
    curl_setopt( $this->ch, CURLOPT_FORBID_REUSE, 1 );
    $this->set_head( [ "CLIENT-IP: $ip", "X-FORWARDED-FOR: $ip" ] );
  }

  public function get_ip() {
    $ip_long = array(
      array( '607649792', '608174079' ), // 36.56.0.0-36.63.255.255
      array( '1038614528', '1039007743' ), // 61.232.0.0-61.237.255.255
      array( '1783627776', '1784676351' ), // 106.80.0.0-106.95.255.255
      array( '2035023872', '2035154943' ), // 121.76.0.0-121.77.255.255
      array( '2078801920', '2079064063' ), // 123.232.0.0-123.235.255.255
      array( '-1950089216', '-1948778497' ), // 139.196.0.0-139.215.255.255
      array( '-1425539072', '-1425014785' ), // 171.8.0.0-171.15.255.255
      array( '-1236271104', '-1235419137' ), // 182.80.0.0-182.92.255.255
      array( '-770113536', '-768606209' ), // 210.25.0.0-210.47.255.255
      array( '-569376768', '-564133889' ), // 222.16.0.0-222.95.255.255
    );
    $rand_key = mt_rand( 0, 9 );
    return $ip = long2ip( mt_rand( $ip_long[ $rand_key ][ 0 ], $ip_long[ $rand_key ][ 1 ] ) );
  }

  public function get_useragent() {
    $arr = array(
      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11',
      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER',
      'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)',
      'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)',
      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER',
      'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; .NET4.0E)',
      'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)',
      'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
      'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)',
      'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
      'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; .NET4.0E)',
      'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1',
      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1',
      'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)',
      'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; .NET4.0E)',
      'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729;Media Center PC 6.0; .NET4.0C; .NET4.0E)',
      'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 SE 2.X MetaSr 1.0',
      'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; SE 2.X MetaSr 1.0)',
      'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:16.0) Gecko/20121026 Firefox/16.0',
      'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre',
      'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0',
      'Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.15) Gecko/20110303 Firefox/3.6.15',
      'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
      'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11',
      'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.16 (KHTML, like Gecko) Chrome/10.0.648.133 Safari/534.16',
      'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)',
      'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)',
      'Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10',
      'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0'
    );
    return $arr[ array_rand( $arr ) ];
  }

  public function encoded( $code = 'UTF-8' ) {
    $encode = mb_detect_encoding( $this->data, [ 'ASCII', 'UTF-8', 'GB2312', 'GBK', 'BIG5' ] );
    //return iconv( $code, 'UTF-8', $this->data );
    if ( $encode != 'UTF-8' ) {
      return mb_convert_encoding( $this->data, $code, $encode );
    } else {
      return $this->data;
    }

  }

  public function compress() {
    $this->data = preg_replace( '/[\\n\\r\\t]+/', ' ', $this->data ); // 清除换行符，清除制表符
    $this->data = preg_replace( '/\\s{2,}/', ' ', $this->data ); // 清除额外的空格
    $this->data = preg_replace( '/>\\s</', '><', $this->data ); // 清除标签间的空格
    $this->data = preg_replace( '/\\/\\*.*?\\*\\//i', '', $this->data ); // 清除 CSS & JS 的注释
    $this->data = preg_replace( '/<!--[^!]*-->/', '', $this->data ); // 清除 HTML 的注释
    return trim( $this->data );
  }

  public function formaturl( $domain ) {

    if ( preg_match_all( "/(<img[^>]+src=[\"|']([^\"]+)[\"|'][^>]*>)|(<a[^>]+href=[\"|']([^\"]+)[\"|'][^>]*>)|(<script[^>]+src=[\"|']([^\"]+)[\"|'][^<]<\/script>)/i", $this->data, $regs ) ) {
      return print_r( $regs );
      foreach ( $regs[ 0 ] as $url ) {
        $this->data = str_replace( $url, $this->parse( $url, $domain ), $this->data );
      }
    }
    return $this->data;
  }

  public function parse( $url, $domain ) {
    if ( preg_match( "/(.*)(href|src)\=(.+?)( |\/\>|\>).*/i", $url, $regs ) ) {
      $I2 = $regs[ 3 ];
    }
    if ( strlen( $I2 ) > 0 ) {
      $I1 = str_replace( chr( 34 ), "", $I2 );
      $I1 = str_replace( chr( 39 ), "", $I1 );
    } else {
      return $url;
    }
    $url_parsed = parse_url( $domain );
    $scheme = $url_parsed[ "scheme" ];
    if ( $scheme != "" ) {
      $scheme = $scheme . "://";
    }
    $host = $url_parsed[ "host" ];
    $l3 = $scheme . $host;
    if ( strlen( $l3 ) == 0 ) {
      return $url;
    }
    $path = dirname( $url_parsed[ "path" ] );
    if ( $path[ 0 ] == "\\" ) {
      $path = "";
    }
    $pos = strpos( $I1, "#" );
    if ( $pos > 0 )$I1 = substr( $I1, 0, $pos );
    //判断类型
    if ( preg_match( "/^(http|https|ftp):(\/\/|\\\\)(([\w\/\\\+\-~`@:%])+\.)+([\w\/\\\.\=\?\+\-~`@\':!%#]|(&)|&)+/i", $I1 ) ) {
      return $url;
    } //http开头的url类型要跳过
    elseif ( $I1[ 0 ] == "/" ) {
        $I1 = $l3 . $I1;

      } //绝对路径
    elseif ( substr( $I1, 0, 3 ) == "../" ) { //相对路径
      while ( substr( $I1, 0, 3 ) == "../" ) {
        $I1 = substr( $I1, strlen( $I1 ) - ( strlen( $I1 ) - 3 ), strlen( $I1 ) - 3 );
        if ( strlen( $path ) > 0 ) {
          $path = dirname( $path );
        }
      }
      $I1 = $l3 . $path . "/" . $I1;
    }
    elseif ( substr( $I1, 0, 2 ) == "./" ) {
      $I1 = $l3 . $path . substr( $I1, strlen( $I1 ) - ( strlen( $I1 ) - 1 ), strlen( $I1 ) - 1 );
    }
    elseif ( strtolower( substr( $I1, 0, 7 ) ) == "mailto:" || strtolower( substr( $I1, 0, 11 ) ) == "javascript:" ) {
      return $url;
    } else {
      $I1 = $l3 . $path . "/" . $I1;
    }
    return str_replace( $I2, "\"$I1\"", $url );
  }

  public function close() {
    curl_close( $this->ch );
    $this->log( 'Close cUrl.' );
    //$this->log( "\r\n\r\n============================================================\r\n\r\n", 1 );
  }

  public function log( $msg, $type = 0 ) {
    if ( $this->debug == 0 ) {
      return;
    }
    if ( $type ) {
      $data = $msg;
    } else {
      $data = '[' . date( 'Y-m-d H:i:s' ) . '] ' . $msg . "\r\n";
    }

    file_put_contents( __DIR__ . '/curl.log', $data, FILE_APPEND );
  }

  public function __destruct() {
    $this->close();
  }
}